// Copyright 2022 The Google Research Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef THIRD_PARTY_DNN_ACCELERATOR_HLS_SYSTEMC_ACCELERATORTOP_H_
#define THIRD_PARTY_DNN_ACCELERATOR_HLS_SYSTEMC_ACCELERATORTOP_H_

#include "src/AccelTypes.h"

#include <mc_connections.h>
#include <systemc.h>


#include "src/ArchitectureParams.h"
#include "src/TensorCore.h"

SC_MODULE(AcceleratorTop) {
  sc_in<bool> CCS_INIT_S1(clk);
  sc_in<bool> CCS_INIT_S1(rstn);

  Connections::In<Params> CCS_INIT_S1(paramsIn);
  Connections::In<VectorParams> CCS_INIT_S1(vectorParamsIn);
  Connections::Out<MemoryRequest> inputAddressRequest[NUM_CORES];
  Connections::In<Pack1D<INPUT_DATATYPE, DIMENSION> >
      inputDataResponse[NUM_CORES];
  Connections::Out<MemoryRequest> weightAddressRequest[NUM_CORES];
  Connections::In<Pack1D<WEIGHT_DATATYPE, DIMENSION> >
      weightDataResponse[NUM_CORES];
  Connections::Out<Pack1D<OUTPUT_DATATYPE, DIMENSION> >
      vectorUnitOutput[NUM_CORES];
  Connections::Out<int> outputAddress[NUM_CORES];
  Connections::Out<int> vectorFetchAddressRequest[NUM_CORES];
  Connections::In<Pack1D<OUTPUT_DATATYPE, DIMENSION> >
      vectorFetchDataResponse[NUM_CORES];
  Connections::SyncOut done[NUM_CORES];

  Connections::Combinational<Params> paramsChannel[NUM_CORES];
  Connections::Combinational<VectorParams> vectorParamsChannel[NUM_CORES];

  SC_CTOR(AcceleratorTop) {
    TensorCore<INPUT_DATATYPE, WEIGHT_DATATYPE, OUTPUT_DATATYPE, DIMENSION,
               DIMENSION, INPUT_BUFFER_SIZE, WEIGHT_BUFFER_SIZE,
               ACCUM_BUFFER_SIZE> *tensorCore[NUM_CORES];
    for (int i = 0; i < NUM_CORES; i++) {
      tensorCore[i] =
          new TensorCore<INPUT_DATATYPE, WEIGHT_DATATYPE, OUTPUT_DATATYPE,
                         DIMENSION, DIMENSION, INPUT_BUFFER_SIZE,
                         WEIGHT_BUFFER_SIZE, ACCUM_BUFFER_SIZE>(
              sc_gen_unique_name("tensorcore_inst"));
      tensorCore[i]->clk(clk);
      tensorCore[i]->rstn(rstn);
      tensorCore[i]->paramsIn(paramsChannel[i]);
      tensorCore[i]->vectorParamsIn(vectorParamsChannel[i]);
      tensorCore[i]->inputAddressRequest(inputAddressRequest[i]);
      tensorCore[i]->inputDataResponse(inputDataResponse[i]);
      tensorCore[i]->weightAddressRequest(weightAddressRequest[i]);
      tensorCore[i]->weightDataResponse(weightDataResponse[i]);
      tensorCore[i]->vectorFetchAddressRequest(vectorFetchAddressRequest[i]);
      tensorCore[i]->vectorFetchDataResponse(vectorFetchDataResponse[i]);
      tensorCore[i]->vectorUnitOutput(vectorUnitOutput[i]);
      tensorCore[i]->outputAddress(outputAddress[i]);
      tensorCore[i]->done(done[i]);
    }

    SC_THREAD(run);
    sensitive << clk.pos();
    async_reset_signal_is(rstn, false);
  }

  void run();
};

#endif  // THIRD_PARTY_DNN_ACCELERATOR_HLS_SYSTEMC_ACCELERATORTOP_H_
